package com.marketlive.system.ajp;

import java.io.IOException;

import org.apache.jk.common.ChannelSocket;
import org.apache.jk.core.JkHandler;
import org.apache.jk.server.JkMain;


/**
 * A service to manage AJP connections running inside Apache MODJK framework, under the
 * supported list (below) of application servers. The service APIs are currently 
 * scoped to managing the lifecycle of AJP connections. Supported operations:
 * 
 * 	enable, disable, pause, resume, isConfigured
 *  
 * The API calls are wired into using the Tomcat JkMain class to access
 * the connector channels. The Tomcat APIs may change over time. It would
 * be better (IMHO) to use JMX to manage these connections, which is possible in a Tomcat
 * container. However I did not successfully run the Tomcat modeler-based JMX beans in a 
 * Glassfish container.  
 * 
 * This service has only been tested with the following versions of Tomcat and Glassfish:
 * 
 *   Tomcat 5.5.16 (tomcat-ajp.jar)
 *   Glassfish 2.1  
 *  
 *  NOTE: Mod JK can use multiple protocols and transports. This service only manages AJP connections.
 *  TODO: Will need to retest in Glassfish 3.
 *  
 *  @author dtaylor
 */
public class AjpServices {
	
	protected static final String CHANNEL_SOCKET = "channelSocket";
	protected static final String MSG_FAIL_CONNECT_JK = "Failed to connect to Mod JK in memory manager";
	protected static final String MSG_FAIL_CONNECT_CHANNEL = "Failed to connect to AJP Channel Socket";
	protected static final String MSG_FAIL_FIND_CHANNEL = "Failed to find to AJP Channel Socket";

	/**
	 * Construct a new AjpServices service. This service is designed to be run inside a Spring 
	 * container, but is not coupled to the Spring framework.
	 */
	public AjpServices() {		
	}

	/**
	 * The name of a system property for wiring in the AJP socket port for this JVM. 
	 */
	public static final String PORT_PROPERTY = "com.marketlive.system.ajp.port";

    /**
     * Disable Port on Shutdown flag ("true" | "false")
     * The system property overriding default behavior of closing the AJP port on shutdown
     * The default behavior is to DISABLE the port on shutdown
     */
    public static final String DISABLE_ON_SHUTDOWN = "com.marketlive.system.ajp.disablePortShutdown";

	/**
	 * Will immediately enable a Tomcat Connector for the given port
	 * The change will not be persisted to any configuration 
	 * 
	 * @param port the new port to open up a connection on
	 * @throws IOException
	 */	
	public void enableConnectorChannel(int port)
		throws IOException
	{
		ChannelSocket socket = acquireConnectorChannel();
		try {
     		socket.setPort(port);
     		socket.init();
     		socket.start();
       	} 
       	catch (Exception e) {
        		throw new IOException(e);
        }
	}

	/**
	 * Will immediately disable a Tomcat Connector 
	 * The change will not be persisted to any configuration 
	 * 
	 * @throws IOException
	 */		
	public void disableConnectorChannel()
	throws IOException
	{
		ChannelSocket socket = acquireConnectorChannel();
		try {
			socket.stop();
	     	socket.destroy();
	    } 
	    catch (Exception e) {
	    	throw new IOException(e);
	    }
	}

	/**
	 * Will immediately pause a Tomcat Connector 
	 * The change will not be persisted to any configuration 
	 * 
	 * @throws IOException
	 */			
	public void pauseConnectorChannel()
	throws IOException
	{
		ChannelSocket socket = acquireConnectorChannel();
    	try {
     		socket.pause();
    	} 
    	catch (Exception e) {
    		throw new IOException(e);
		}
	}

	/**
	 * Will immediately resume a paused Tomcat Connector 
	 * The change will not be persisted to any configuration 
	 * 
	 * @throws IOException
	 */				
	public void resumeConnectorChannel()
	throws IOException
	{
		ChannelSocket socket = acquireConnectorChannel();
    	try {
     		socket.resume();
    	} 
    	catch (Exception e) {
    		throw new IOException(e);
		}
	}

	/**
	 * Determine if Mod JK is configured in this JVM. Note we first check for 
	 * the system property "com.marketlive.system.ajp.port" being set, if it
	 * is not set, then the configured status is false, since this 
	 * service is not properly configured
	 * 
	 * @return true if mod jk installation is detected, otherwise false
	 */
	public boolean isConfigured()
	{
    	String port = System.getProperty(PORT_PROPERTY);
    	if (port == null || port.trim().equals(""))
    		return false;
		JkMain jk = JkMain.getJkMain();
	    if (jk == null) 
	    	return false;	    
	    JkHandler handler = jk.getWorkerEnv().getHandler(CHANNEL_SOCKET);
	    if (handler == null) 
	    	return false;        		    
	    if (handler instanceof ChannelSocket) 
	    	return true;	    
	    return false;	    
	}	

    public boolean isDisableOnShutdown()
    {
        String property = System.getProperty(DISABLE_ON_SHUTDOWN);
        if (property == null || property.trim().equals(""))
            return true;
        return property.equalsIgnoreCase("true");
    }

	/**
	 * helper to acquire an AJP socket connection
	 * @return ChannelSocket connection. Note ChannelSocket is Tomcat-specific
	 * @throws IOException
	 */
	protected ChannelSocket acquireConnectorChannel()
	throws IOException
	{
	    JkMain jk = JkMain.getJkMain();
	    if (jk == null) {
	    	throw new IOException(MSG_FAIL_CONNECT_JK);
	    }
	    JkHandler handler = jk.getWorkerEnv().getHandler(CHANNEL_SOCKET);
	    if (handler == null) {
	    	throw new IOException(MSG_FAIL_CONNECT_CHANNEL);        	
	    }
	    if (handler instanceof ChannelSocket) {
	    	return (ChannelSocket)handler;
	    }
	    throw new IOException(MSG_FAIL_FIND_CHANNEL);	    
	}	
}
